﻿# DisconnectIdleSharedConnections.ps1
# 以管理员权限运行此脚本
# 功能：断开空闲的共享连接，确保连接数不超过指定数量（默认15个）
#
# 使用方法:
# 1. 右键点击PowerShell，选择"以管理员身份运行"
# 2. 导航到脚本所在目录
# 3. 执行 .\DisconnectIdleSharedConnections.ps1
#
# 可选参数:
# -MaxConnections <数字>       # 设置最大连接数，默认为15
# -CheckIntervalSeconds <秒>   # 设置检查间隔，默认为60秒
#
# 示例:
# .\DisconnectIdleSharedConnections.ps1 -MaxConnections 10 -CheckIntervalSeconds 30
#
# 按 Ctrl+C 停止脚本执行

param(
    [int]$MaxConnections = 0,
    [int]$CheckIntervalSeconds = 60
)

# 检查是否以管理员权限运行
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
    Write-Host "请以管理员权限运行此脚本！" -ForegroundColor Red
    Write-Host "右键点击PowerShell或命令提示符，选择'以管理员身份运行'，然后再执行此脚本。" -ForegroundColor Yellow
    exit 1
}

# 检查SMB服务是否正在运行
try {
    $smbService = Get-Service -Name LanmanServer -ErrorAction Stop
    if ($smbService.Status -ne 'Running') {
        Write-Host "警告: SMB服务(LanmanServer)未运行，正在尝试启动..." -ForegroundColor Yellow
        Start-Service -Name LanmanServer -ErrorAction Stop
        Write-Host "✓ SMB服务已启动" -ForegroundColor Green
    }
} catch {
    Write-Host "✗ 无法检查或启动SMB服务: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "请确保SMB共享功能已启用。" -ForegroundColor Yellow
    exit 1
}

Write-Host "共享连接数限制脚本已启动" -ForegroundColor Green
Write-Host "最大连接数: $MaxConnections 个" -ForegroundColor Yellow
Write-Host "检查间隔: $CheckIntervalSeconds 秒" -ForegroundColor Yellow
Write-Host "按 Ctrl+C 停止脚本" -ForegroundColor Cyan

try {
    while ($true) {
        Write-Host "========================================"
        # 获取当前所有SMB会话
        try {
            # 获取本机IP地址列表
            $localIPs = @("127.0.0.1", "::1")
            try {
                $networkInterfaces = Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.AddressState -eq "Preferred" }
                $localIPs += $networkInterfaces.IPAddress
            } catch {
                Write-Host "调试信息: 获取本机IP地址时出错: $($_.Exception.Message)" -ForegroundColor Yellow
            }
            
            # 获取所有SMB会话，包括本地连接
            $sessions = Get-SmbSession -ErrorAction Stop
            
            # 显示所有会话的详细信息用于调试
            #Write-Host "调试信息: 获取到的所有会话详情:" -ForegroundColor Cyan
            foreach ($session in $sessions) {
                Write-Host "  连接IP: '$($session.ClientComputerName)'" -ForegroundColor Cyan
            }
            
            #Write-Host "调试信息: 成功获取SMB会话" -ForegroundColor Green
            Write-Host "调试信息: 会话总数: $($sessions.Count)" -ForegroundColor Cyan
            
            # 显示所有会话详情
            #Write-Host "调试信息: 所有会话详情:" -ForegroundColor Cyan
            if ($sessions -ne $null) {
                foreach ($session in $sessions) {
            #        Write-Host "  连接IP: '$($session.ClientComputerName)', 空闲时间: '$($session.IdleTime)'" -ForegroundColor Cyan
                }
            } else {
            #    Write-Host "  没有会话" -ForegroundColor Cyan
            }
        } catch {
            Write-Host "调试信息: 获取SMB会话时出错: $($_.Exception.Message)" -ForegroundColor Red
            $sessions = $null
        }
        
        # 处理会话计数
        if ($null -eq $sessions) {
            $currentSessionCount = 0
            #Write-Host "调试信息: 未获取到任何SMB会话" -ForegroundColor Yellow
        } else {
            # 添加详细调试信息
            #Write-Host "调试信息: sessions 变量类型 = $($sessions.GetType().Name)" -ForegroundColor Magenta
            #Write-Host "调试信息: sessions 变量值 = $sessions" -ForegroundColor Magenta
            
            # 检查$sessions是否为数组或集合
            if ($sessions -is [array] -or $sessions -is [System.Collections.IEnumerable]) {
                $currentSessionCount = $sessions.Count
                #Write-Host "调试信息: 检测到 $currentSessionCount 个会话 (数组/集合类型)" -ForegroundColor Cyan
            } else {
                # 单个对象的情况
                $currentSessionCount = 1
                #Write-Host "调试信息: 检测到 1 个会话 (单对象类型)" -ForegroundColor Cyan
            }
        }
        
        # 添加详细的调试信息
        #Write-Host "调试信息: currentSessionCount 变量值 = $currentSessionCount" -ForegroundColor Magenta
        #Write-Host "调试信息: currentSessionCount 变量类型 = $($currentSessionCount.GetType().Name)" -ForegroundColor Magenta
        
        # 尝试不同的方式显示变量
        #Write-Host "调试信息: 直接插值显示 = $currentSessionCount" -ForegroundColor Cyan
        #Write-Host "调试信息: 强制字符串转换 = $([string]$currentSessionCount)" -ForegroundColor Cyan
        
        # 使用字符串格式化确保变量正确显示
        #$displayMessage = "当前活动连接: {0} 个 - {1}" -f $currentSessionCount, (Get-Date -Format 'HH:mm:ss')
        Write-Host $displayMessage -ForegroundColor Gray
        
        # 如果连接数超过最大限制，则按空闲时间排序并断开最空闲的连接
        # 当MaxConnections=0时，断开所有连接
        #Write-Host "调试信息: 检查是否需要断开连接 - 当前连接数: $currentSessionCount, 最大连接数: $MaxConnections" -ForegroundColor Yellow
        if ($currentSessionCount -gt $MaxConnections) {
            $disconnectedCount = 0
            # 当MaxConnections=0时，$excessCount应该等于$currentSessionCount（断开所有连接）
            $excessCount = $currentSessionCount - $MaxConnections
            #Write-Host "调试信息: excessCount = $excessCount" -ForegroundColor Magenta
            
            # 添加额外的调试信息
            #Write-Host "调试信息: currentSessionCount 类型 = $($currentSessionCount.GetType().Name)" -ForegroundColor Magenta
            #Write-Host "调试信息: MaxConnections 类型 = $($MaxConnections.GetType().Name)" -ForegroundColor Magenta
            
            Write-Host "发现连接数超限: 当前 $currentSessionCount 个, 最大 $MaxConnections 个, 需要断开 $excessCount 个" -ForegroundColor Yellow
            
            # 确保$sessions是数组形式，以便正确处理
            if ($null -ne $sessions) {
                # 检查是否已经是数组或集合
                if ($sessions -is [array] -or $sessions -is [System.Collections.IEnumerable]) {
                    $sessionArray = $sessions
                    #Write-Host "调试信息: sessions 已经是数组类型" -ForegroundColor Green
                } else {
                    # 单个对象的情况，转换为数组
                    $sessionArray = @($sessions)
                    #Write-Host "调试信息: 将单个会话对象转换为数组" -ForegroundColor Green
                }
            } else {
                $sessionArray = @()
                #Write-Host "调试信息: sessions 为 null，创建空数组" -ForegroundColor Yellow
            }
            
            # 添加调试信息
            #Write-Host "调试信息: sessionArray.Count = $($sessionArray.Count)" -ForegroundColor Cyan
            #Write-Host "调试信息: sessionArray 类型 = $($sessionArray.GetType().Name)" -ForegroundColor Cyan
            
            # 检查是否有会话需要断开
            if ($sessionArray.Count -gt 0) {
                # 添加更多调试信息
                #Write-Host "调试信息: sessionArray 中第一个会话的类型 = $($sessionArray[0].GetType().Name)" -ForegroundColor Magenta
               # Write-Host "调试信息: sessionArray 中第一个会话的 IdleTime = '$($sessionArray[0].IdleTime)'" -ForegroundColor Magenta
                if ($sessionArray[0].IdleTime -ne $null) {
                    #Write-Host "调试信息: sessionArray 中第一个会话的 IdleTime 类型 = '$($sessionArray[0].IdleTime.GetType().Name)'" -ForegroundColor Magenta
                } else {
                    #Write-Host "调试信息: sessionArray 中第一个会话的 IdleTime 为 Null" -ForegroundColor Magenta
                }
                
                # 按空闲时间降序排列（空闲时间最长的在前面）
                try {
                    #Write-Host "调试信息: 开始排序会话..." -ForegroundColor Yellow
                    
                    # 先检查是否有会话的IdleTime为空，如果有则设置默认值
                    foreach ($session in $sessionArray) {
                        if ($session.IdleTime -eq $null) {
                            #Write-Host "调试信息: 发现空的IdleTime，设置为0" -ForegroundColor Yellow
                            # 注意：我们不能直接修改CimInstance对象的属性
                        }
                    }
                    
                    # 使用自定义比较函数进行排序
                    #Write-Host "调试信息: 使用自定义比较函数进行排序" -ForegroundColor Yellow
                    $sortedSessions = $sessionArray | Sort-Object { 
                        if ($_.IdleTime -eq $null) { 
                            0 
                        } else { 
                            $_.IdleTime.TotalMinutes 
                        } 
                    } -Descending
                    
                    # 确保$sortedSessions是数组形式
                    if ($sortedSessions -isnot [array] -and $sortedSessions -isnot [System.Collections.IEnumerable]) {
                        if ($sortedSessions -ne $null) {
                            $sortedSessions = @($sortedSessions)
                            #Write-Host "调试信息: 将单个排序结果转换为数组" -ForegroundColor Green
                        } else {
                            $sortedSessions = @()
                            #Write-Host "调试信息: 排序结果为null，创建空数组" -ForegroundColor Yellow
                        }
                    }
                    
                    #Write-Host "调试信息: sortedSessions.Count = $($sortedSessions.Count)" -ForegroundColor Cyan
                    #Write-Host "调试信息: sortedSessions 类型 = $($sortedSessions.GetType().Name)" -ForegroundColor Cyan
                } catch {
                    Write-Host "调试信息: 排序会话时出错: $($_.Exception.Message)" -ForegroundColor Red
                    Write-Host "调试信息: 异常类型: $($_.Exception.GetType().Name)" -ForegroundColor Red
                    # 如果排序失败，使用原始会话数组
                    $sortedSessions = $sessionArray
                    #Write-Host "调试信息: 使用原始会话数组，Count = $($sortedSessions.Count)" -ForegroundColor Yellow
                }
                
                # 断开空闲时间最长的 excessCount 个连接
                #Write-Host "调试信息: excessCount = $excessCount, sortedSessions.Count = $($sortedSessions.Count)" -ForegroundColor Magenta
                $sessionsToDisconnect = [math]::Min($excessCount, $sortedSessions.Count)
                #Write-Host "调试信息: 准备断开 $sessionsToDisconnect 个会话" -ForegroundColor Cyan
                
                # 添加额外的边界检查
                if ($sessionsToDisconnect -lt 0) {
                    #Write-Host "调试信息: sessionsToDisconnect 为负数，设置为0" -ForegroundColor Red
                    $sessionsToDisconnect = 0
                }
                
                if ($sessionsToDisconnect -gt $sortedSessions.Count) {
                    #Write-Host "调试信息: sessionsToDisconnect 超出范围，设置为sortedSessions.Count" -ForegroundColor Red
                    $sessionsToDisconnect = $sortedSessions.Count
                }
                
                # 添加额外的调试信息
                #Write-Host "调试信息: excessCount 类型 = $($excessCount.GetType().Name)" -ForegroundColor Magenta
                #Write-Host "调试信息: sortedSessions.Count 类型 = $($sortedSessions.Count.GetType().Name)" -ForegroundColor Magenta
                
                # 额外检查sortedSessions是否有效
                if ($null -eq $sortedSessions) {
                    #Write-Host "调试信息: sortedSessions 为 null" -ForegroundColor Red
                    $sortedSessions = @()  # 设置为空数组防止后续错误
                }
                
                for ($i = 0; $i -lt $sessionsToDisconnect; $i++) {
                    # 添加索引调试信息
                    #Write-Host "调试信息: 处理第 $i 个会话，sessionsToDisconnect = $sessionsToDisconnect" -ForegroundColor Cyan
                    
                    # 检查索引是否有效
                    if ($i -ge $sortedSessions.Count) {
                        #Write-Host "调试信息: 索引 $i 超出范围，sortedSessions.Count = $($sortedSessions.Count)" -ForegroundColor Red
                        continue
                    }
                    
                    $session = $sortedSessions[$i]
                    
                    # 检查会话对象是否有效
                    if ($null -eq $session) {
                        #Write-Host "调试信息: 第 $i 个会话对象为 null" -ForegroundColor Red
                        continue
                    }
                    
                    # 输出会话的所有属性用于调试
                    #Write-Host "调试信息: 会话对象属性:" -ForegroundColor Magenta
                    #$session.PSObject.Properties | ForEach-Object {
                    #    Write-Host "  $($_.Name) = $($_.Value)" -ForegroundColor Magenta
                    #}
                    
                    # 计算空闲时间（分钟）
                    #Write-Host "调试信息: 计算空闲时间前检查 - IdleTime = '$($session.IdleTime)'" -ForegroundColor Cyan
                    $idleTime = if ($session.IdleTime -ne $null -and $session.IdleTime.TotalMinutes -ne $null) { 
                        [math]::Round($session.IdleTime.TotalMinutes, 2) 
                    } else { 
                        0 
                    }
                    #Write-Host "调试信息: 计算后的空闲时间 = $idleTime" -ForegroundColor Cyan
                    
                    #Write-Host "断开空闲连接: IP地址 $($session.ClientComputerName) 空闲 $idleTime 分钟" -ForegroundColor Yellow
                    
                    try {
                        # 断开会话
                        #Write-Host "调试信息: 正在尝试断开 SessionId = $($session.SessionId)" -ForegroundColor Cyan
                        #Write-Host "调试信息: SessionId 类型 = '$($session.SessionId.GetType().Name)'" -ForegroundColor Magenta
                        #Write-Host "调试信息: SessionId 值 = '$($session.SessionId)'" -ForegroundColor Magenta
                        
                        Close-SmbSession -SessionId $session.SessionId -Force
                        Write-Host "✓ 已断开连接: $($session.ClientComputerName)" -ForegroundColor Green
                        $disconnectedCount++
                    }
                    catch {
                        Write-Host "✗ 断开失败: $($session.ClientComputerName) - $($_.Exception.Message)" -ForegroundColor Red
                    }
                }
                
                if ($disconnectedCount -gt 0) {
                    Write-Host "本次检查断开了 $disconnectedCount 个空闲连接" -ForegroundColor Green
                }
            } else {
                Write-Host "警告: 没有找到可断开的会话" -ForegroundColor Yellow
            }
        }
        
        Write-Host "########################################"
        # 等待下一次检查
        Start-Sleep -Seconds $CheckIntervalSeconds
    }
}
catch {
    Write-Host "脚本执行出错: $($_.Exception.Message)" -ForegroundColor Red
}
finally {
    Write-Host "脚本已停止" -ForegroundColor Cyan
}